﻿using AutoMapper;
using EasyCore.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Resources;
using System.Text;
using System.Threading.Tasks;

namespace EasyCore.ObjectMapper
{
    /// <summary>
    /// 对象映射扩展操作
    /// </summary>
    public static class MapperExtensions
    {
        private static IMapper _mapper { get; set; }

        /// <summary>
        /// 将模块服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public static IServiceCollection AddObjectMapper(this IServiceCollection services)
        {
            var configuration = new MapperConfiguration(configExp =>
            {
                var types = AssemblyManager.FindTypesByBase(typeof(AutoMapperTupleBase)).ToList(); //抽象类
                List<AutoMapperTupleBase> mapTuples = new List<AutoMapperTupleBase>();
                foreach (var type in types)
                {
                    var constructor = type.GetConstructor(new Type[] { }); //获得构造函数
                    AutoMapperTupleBase mapTuple = (AutoMapperTupleBase)constructor.Invoke(new object[] { });
                    mapTuples.Add(mapTuple);
                }
                mapTuples = mapTuples.OrderBy(m => m.Order).ToList();
                foreach (AutoMapperTupleBase mapTuple in mapTuples)
                {
                    configExp.AddProfile(mapTuple);
                }
                //var tuples = AssemblyManager.FindTypesByBase(typeof(IMapTuple)).ToList(); //非抽象类
                //foreach (Type mapTuple in tuples)
                //{

                //    configExp.AddProfile(mapTuple);
                //}
            });
            AutoMapperMapper mapper = new AutoMapperMapper(configuration);
            services.AddSingleton<IMapper>(mapper);
            return services;
        }

        /// <summary>
        /// 应用模块服务
        /// </summary>
        /// <param name="provider">应用程序</param>
        public static void UseObjectMapper(this WebApplication app)
        {
            IMapper mapper = app.Services.GetRequiredService<IMapper>();
            MapperExtensions.SetMapper(mapper); //设置静态映射
        }

        /// <summary>
        /// 设置对象映射执行者
        /// </summary>
        /// <param name="mapper">映射执行者</param>
        public static void SetMapper(IMapper mapper)
        {
            _mapper = mapper;
        }

        /// <summary>
        /// 将对象映射为指定类型
        /// </summary>
        /// <typeparam name="TTarget">要映射的目标类型</typeparam>
        /// <param name="source">源对象</param>
        /// <returns>目标类型的对象</returns>
        public static TTarget MapTo<TTarget>(this object source)
        {
            CheckMapper();
            return _mapper.MapTo<TTarget>(source);
        }

        /// <summary>
        /// 使用源类型的对象更新目标类型的对象
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TTarget">目标类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">待更新的目标对象</param>
        /// <returns>更新后的目标类型对象</returns>
        public static TTarget MapTo<TSource, TTarget>(this TSource source, TTarget target)
        {
            CheckMapper();
            return _mapper.MapTo(source, target);
        }

        /// <summary>
        /// 将数据源映射为指定<typeparamref name="TOutputDto"/>的集合
        /// </summary>
        /// <param name="source">数据源</param>
        /// <param name="membersToExpand">成员展开</param>
        public static IQueryable<TOutputDto> ToOutput<TEntity, TOutputDto>(this IQueryable<TEntity> source,
            params Expression<Func<TOutputDto, object>>[] membersToExpand)
        {
            CheckMapper();
            return _mapper.ToOutput<TOutputDto>(source, membersToExpand);
        }

        /// <summary>
        /// 验证映射执行者是否为空
        /// </summary>
        private static void CheckMapper()
        {
            if (_mapper == null)
            {
                throw new NullReferenceException("对象为空，无法进行映射");
            }
        }
    }
}
